package com.example.controller;


import com.example.common.CommonError;
import com.example.common.CommonRes;
import com.example.common.BusinessError;
import com.example.config.AppProperties;
import com.example.dao.Permission;
import com.example.dao.Role;
import com.example.dao.User;
import com.example.dao.dto.UserDto;
import com.example.service.*;
import com.example.util.JwtUtil;
import com.example.util.TotpUtil;
import com.example.validation.DtoValidator;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 登录和刷新token
 */
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/authorize")
public class AuthorizeResource {
    /**
     * 用户service
     */
    @Autowired
    private UserService userService;
    /**
     * 用户service
     */
    @Autowired
    private RoleService roleService;
    /**
     * 用户service
     */
    @Autowired
    private PermissionService
            permissionService;
//    @Autowired
//    private UserCacheService userCacheService;
    /**
     * 短信service
     */
    private SmsService smsService;
    /**
     * email的service
     */
    private EmailService emailService;
    /**
     * 国际化MessageSource
     */
//    @Autowired
//    private MessageSource messageSource;
    /**
     * 属性配置
     */
    @Autowired
    private AppProperties appProperties;
    /**
     * token公共类
     */
    @Autowired
    private JwtUtil jwtUtil;

    /**
     * 一次性验证工具类
     */
    @Autowired
    private TotpUtil totpUtil;

    /**
     * 加密处理器
     */
    @Autowired
    private PasswordEncoder passwordEncoder;

    /**
     * 校验器
     */
    @Autowired
    private DtoValidator dtoValidator;


    /**
     * 校验用户账号，用户账号不能重复
     *
     * @param username
     * @return
     */
    @GetMapping("/validateUsername")
    public CommonRes validateUsername(@RequestParam String username) {
        if (userService.isUsernameExisted(username, 0l)) {
            return CommonRes.createError(null);
        } else {
            return CommonRes.create(null);
        }
    }

    /**
     * 校验邮箱，邮箱不能重复
     *
     * @param email
     * @return
     */
    @GetMapping("/validateEmail")
    public CommonRes validateEmail(@RequestParam String email) {
        if (userService.isEmailExisted(email, 0l)) {
            return CommonRes.createError(null);
        } else {
            return CommonRes.create(null);
        }
    }

    /**
     * 校验手机，手机不能重复
     *
     * @param mobile
     * @return
     */
    @GetMapping("/validateMobile")
    public CommonRes validateMobile(@RequestParam String mobile) {
        if (userService.isMobileExisted(mobile, 0l)) {
            return CommonRes.createError(null);
        } else {
            return CommonRes.create(null);
        }
    }

    @PostMapping("/register")
    public CommonRes addRegister(@Valid @RequestBody UserDto userDto) {
        // com.example.aspect.ValidateAspect
        //通过aop校验传过来的用户实体类userDto

        //账号不能重复
        if (userService.isUsernameExisted(userDto.getUsername(), 0l)) {
            CommonError commonError = new CommonError(BusinessError.DUULICATE_USERNAME);
            return CommonRes.createError(commonError);
        }
        //用户邮箱不能重复
        if (userService.isEmailExisted(userDto.getEmail(), 0l)) {
            CommonError commonError = new CommonError(BusinessError.DUULICATE_EMAIL);
            return CommonRes.createError(commonError);
        }
        //用户手机不能重复
        if (userService.isMobileExisted(userDto.getMobile(), 0l)) {
            CommonError commonError = new CommonError(BusinessError.DUULICATE_MOBILE);
            return CommonRes.createError(commonError);
        }

        User user = new User();
        user.setMfaKey(totpUtil.encodeKeyToString());
        user.setPassword(passwordEncoder.encode(userDto.getPassword()));
        //用于一次性验证码
        user.setMfaKey(totpUtil.encodeKeyToString());
        user.setUsername(userDto.getUsername());
        user.setName(userDto.getName());
        user.setEmail(userDto.getEmail());
        user.setMobile(userDto.getMobile());
        user.setUsingMfa(true);
        int result = userService.registerUser(user);
        if (result > 0) {
            return CommonRes.create(null);
        } else {
            CommonError commonError = new CommonError(BusinessError.UNKNOWN_ERROR);
            return CommonRes.createError(commonError);
        }
    }

    /**
     * 登录
     *
     * @param userParam
     * @return
     */
    @PostMapping("/token")
    public CommonRes login(@RequestBody User userParam) {
        User user = userService.findByUsernameAndPassword(userParam.getUsername(), userParam.getPassword());
        //用户为空，没找到相应的用户
        if (user == null) {
            CommonError commonError = new CommonError(BusinessError.NAME_OR_PASS_ERROR);
            return CommonRes.createError(commonError);
        }
        //用户没有激活
        if (!user.isEnabled()) {
            CommonError commonError = new CommonError(BusinessError.USER_DISABLED);
            return CommonRes.createError(commonError);
        }
        //用户账号锁定
        if (!user.isAccountNonLocked()) {
            CommonError commonError = new CommonError(BusinessError.ACCOUNT_LOCKED);
            return CommonRes.createError(commonError);
        }
        //用户账号过期
        if (!user.isAccountNonExpired()) {
            CommonError commonError = new CommonError(BusinessError.ACCOUNT_EXPIRED);
            return CommonRes.createError(commonError);
        }
        //用户密码过期
        if (!user.isCredentialsNonExpired()) {
            CommonError commonError = new CommonError(BusinessError.CREDENTIALS_EXPIRED);
            return CommonRes.createError(commonError);
        }

        //找到用户的所有角色
        List<Role> roleList = roleService.findRolesByUser(user);
        //由于一个用户可以对应多个角色，不同角色可以对应相同的permission，所以需要用到set达到去重的目的
        //一级目录
        Set<Permission> permissionFirst = new HashSet<>();
        //二级目录
        Set<Permission> permissionSecond = new HashSet<>();
        //所有角色对应的权限列表，可以有重复
        List<Permission> allPermissionList=new ArrayList<>();
        for (Role role : roleList) {
            //找到角色对应的目录
            List<Permission> permissionList = permissionService.findPermissionsByRole(role);
            allPermissionList.addAll(permissionList);
            for (Permission permission : permissionList) {
                if (permission.getPid() == null) {
                    permissionFirst.add(permission);
                } else {
                    permissionSecond.add(permission);
                }
            }
        }

        for(Permission ps:permissionSecond){
            Integer operation=ps.getOperation();
            for(Permission pa:allPermissionList){
                //若有重复，权限的操作属性叠加
                if(ps.equals(pa)){
                  operation|=pa.getOperation();
                }
            }
            ps.setOperation(operation);
        }

        //一级目录进行排序，这里不能用treemap，经测试，treemap在orderNumber相同的情况下，有数据丢失
        List<Permission> firstList = new ArrayList<>();
        firstList.addAll(permissionFirst.stream().sorted().collect(Collectors.toList()));
        for (Permission permission : firstList) {
            List<Permission> childList = new ArrayList<>();
            for (Permission second : permissionSecond) {
                //相对应的二级目录
                if (second.getPid() == permission.getId()) {
                    childList.add(second);
                }
            }
            //二级目录排序
            permission.getChildren().addAll(childList.stream().sorted().collect(Collectors.toList()));
        }

        Set<Role> roleSet = new HashSet<>(roleList);
        user.setAuthorities(roleSet);
        //密码处理
        user.setPassword("****");
        //生成token
        String token = jwtUtil.createAccessToken(user);
        //生成刷新token
        String refreshToken = jwtUtil.createRefreshToken(user);
        //登录数据
        LoginUserData loginUserData = new LoginUserData();
        loginUserData.setToken(token);
        loginUserData.setRefreshToken(refreshToken);
        loginUserData.setUser(user);
        loginUserData.setPermissionSet(permissionFirst);
        return CommonRes.create(loginUserData);
//                // 不使用多因子认证
//                if (!user.isUsingMfa()) {
        //    return ResponseEntity.ok().body(userService.login(u));
//                }
        // 使用多因子认证
//                val mfaId = userCacheService.cacheUser(user);
//                return ResponseEntity
//                    .status(HttpStatus.UNAUTHORIZED)
//                    .header("X-Authenticate", "mfa", "realm=" + mfaId)
//                    .build();

    }

    /**
     * 刷新token
     *
     * @param authorization
     * @param refreshToken
     * @return
     */
    @PostMapping("/refreshToken")
    public CommonRes refreshToken(@RequestHeader(name = "Authorization") String authorization, @RequestParam String refreshToken) {
        //获取token并去掉头部
        String accessToken = authorization.replace(appProperties.getJwt().getPrefix(), "");
        //校验token及refreshToken
        if (!jwtUtil.validateRefreshToken(refreshToken) && !jwtUtil.validateWithoutExpiration(accessToken)) {
            CommonError commonError = new CommonError(BusinessError.ACCESS_DENIED_ERROR);
            return CommonRes.createError(commonError);
        }
        //重新获取token
        accessToken = jwtUtil.buildAccessTokenWithRefreshToken(refreshToken);
        return CommonRes.create(accessToken);
    }

   /* @PostMapping("/token/refresh")
    public Auth refreshToken(@RequestHeader(name = "Authorization") String authorization, @RequestParam String refreshToken) {
        val accessToken = authorization.replace(appProperties.getJwt().getPrefix(), "");
        return userService.refreshToken(accessToken, refreshToken);
    }

    @PutMapping("/totp")
    public void sendTotp(@Valid @RequestBody SendTotpDto sendTotpDto) {
        userCacheService.retrieveUser(sendTotpDto.getMfaId())
                .flatMap(user -> userService.createTotp(user).map(code -> Pair.of(user, code)))
                .ifPresentOrElse(pair -> {
                    log.debug("totp: {}", pair.getSecond());
                    if (sendTotpDto.getMfaType() == MfaType.SMS) {
                        smsService.send(pair.getFirst().getMobile(), pair.getSecond());
                    } else {
                        emailService.send(pair.getFirst().getEmail(), pair.getSecond());
                    }
                }, () -> {
                    throw new InvalidTotpProblem();
                });
    }

    @PostMapping("/totp")
    public Auth verifyTotp(@Valid @RequestBody TotpVerificationDto totpVerificationDto) {
        return userCacheService.verifyTotp(totpVerificationDto.getMfaId(), totpVerificationDto.getCode())
                .map(User::getUsername)
                .flatMap(userService::findOptionalByUsername)
                .map(userService::loginWithTotp)
                .orElseThrow(InvalidTotpProblem::new);
    }*/

    @Data
    private static class LoginUserData {
        /**
         * token 能访问资源
         */
        private String token;
        /**
         * 刷新token，只能刷新token，不能访问资源
         */
        private String refreshToken;
        /**
         * 用户数据
         */
        private User user;
        /**
         * 一级目录，包含有二级目录
         */
        private Set<Permission> permissionSet;
    }
}
